//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using System.Resources;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using System.Globalization;
    using TridentAPI;
    using Microsoft.Research.DataLayer;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;
    using System.Drawing;

    /// <summary>
    /// Selects the appropriate template to be applied.
    /// </summary>
    public class PropertyValueTemplateSelector : DataTemplateSelector
    {
        /// <summary>
        /// Gets the template based on some conditions.
        /// </summary>
        /// <param name="item">
        /// The item for which the template is to be applied.
        /// </param>
        /// <param name="container">
        /// Container.
        /// </param>
        /// <returns>
        /// Appropriate template.
        /// </returns>
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string templateKey = "parameterLayout";
            ParameterDescriptionModel currentParam = item as ParameterDescriptionModel;
            if (currentParam != null)
            {
                if (!currentParam.IsDatabound && (currentParam.DataType.Equals("System.Boolean") || currentParam.DataType.Equals("System.Enum")))
                {
                    templateKey = "parameterLayoutDropDown";
                }
            }
            else
            {
                return null;
            }

            return (DataTemplate)((FrameworkElement)container).FindResource(templateKey);
        }
    }

    /// <summary>
    /// Selects the appropriate template to be applied.
    /// </summary>
    public class ParameterTemplateSelector : DataTemplateSelector
    {
        /// <summary>
        /// Gets the template based on some conditions.
        /// </summary>
        /// <param name="item">
        /// The item for which the template is to be applied.
        /// </param>
        /// <param name="container">
        /// Container.
        /// </param>
        /// <returns>
        /// Appropriate template.
        /// </returns>
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            string templateKey = "ParentActivityTemplate";
            if (!PropertiesPane.ShowOptionalProperties)
            {
                templateKey = "ParentActivityTemplateMandatory";
            }
            else if (PropertiesPane.IsErrorPopup)
            {
                templateKey = "ParentActivityTemplateWrong";
            }

            return (DataTemplate)((FrameworkElement)container).FindResource(templateKey);
        }
    }

    /// <summary>
    /// Creates an appropriate tooltip.
    /// </summary>
    public class PropertyTooltipGenerator : IValueConverter
    {
        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            ParameterDescriptionModel paramModel = value as ParameterDescriptionModel;
            if (paramModel != null)
            {
                return PropertyTooltipGenerator.CreateParamTooltip(paramModel);
            }

            return value;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        /// <summary>
        /// Creates Tooltip for Parameter
        /// </summary>
        private static StackPanel CreateParamTooltip(ParameterDescriptionModel paramModel)
        {
            StackPanel topPanel = new StackPanel();
            TextBlock header = new TextBlock();
            TextBlock data = new TextBlock();
            TextBlock description = new TextBlock();
            TextBlock mandatory = new TextBlock();

            header.FontWeight = FontWeights.Bold;
            header.Text = paramModel.PropertyName;

            data.FontStyle = FontStyles.Italic;
            if (null != paramModel.DataType)
            {
                data.Text = paramModel.DataType.ToString();
            }

            MultiBinding multiBind = new MultiBinding();
            multiBind.Converter = new MandatoryTextGenerator();
            multiBind.ConverterParameter = paramModel;

            Binding mandatoryBinding = new Binding("IsMandatory");
            mandatoryBinding.Mode = BindingMode.OneWay;
            mandatoryBinding.Source = paramModel;
            mandatoryBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;

            Binding mandatoryBinding1 = new Binding("IsMandatoryForWorkflow");
            mandatoryBinding.Mode = BindingMode.OneWay;
            mandatoryBinding.Source = paramModel;
            mandatoryBinding.UpdateSourceTrigger = UpdateSourceTrigger.Default;

            multiBind.Bindings.Add(mandatoryBinding);
            multiBind.Bindings.Add(mandatoryBinding1);

            mandatory.SetBinding(TextBlock.TextProperty, multiBind);
            mandatory.FontStyle = FontStyles.Italic;

            if (null != paramModel.Description)
            {
                description.Text = paramModel.Description;
            }

            topPanel.Children.Add(header);
            topPanel.Children.Add(data);
            topPanel.Children.Add(description);
            topPanel.Children.Add(mandatory);
            return topPanel;
        }
    }

    /// <summary>
    /// Converts various values based on the parameters.
    /// </summary>
    public class TridentConverter : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="values">
        /// The current values.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            object retVal = null;
            if (parameter != null && values != null && values.Length > 0)
            {
                ParameterDescriptionModel paramModel = values[0] as ParameterDescriptionModel;
                DatabindModel dataBindModel = null;
                if (paramModel != null)
                {
                    switch (parameter.ToString())
                    {
                        case "CustomTypeTextBox":
                            try
                            {
                                TypeInitializer typeInit = TypeProviderHelper.FetchTypeInitializer(paramModel.DataType, Runtime.WPF);
                                if (typeInit != null)
                                {
                                    string deserializedVal = (paramModel.Value != null) ? paramModel.Value.ToString() : string.Empty;
                                    BaseTypeInitializer baseTypeinit = ActivityComposer.LoadInitializer(typeInit, deserializedVal);
                                    if (baseTypeinit != null)
                                    {
                                        retVal = baseTypeinit.GetStringValue();
                                    }
                                }
                            }
                            catch (Exception)
                            {
                            }

                            break;

                        case "ReadOnlyBoundTextBox":
                            if (values.Length > 1)
                            {
                                dataBindModel = values[1] as DatabindModel;
                                if (dataBindModel != null)
                                {
                                    StringBuilder sBuilder = new StringBuilder();
                                    sBuilder.Append(dataBindModel.SourceActivityName);
                                    sBuilder.Append(".");
                                    sBuilder.Append(dataBindModel.SourcePropertyName);
                                    retVal = sBuilder.ToString();
                                }
                            }

                            break;

                        case "ReadOnlyBoundTextBoxTooltip":
                            if (values.Length > 1)
                            {
                                dataBindModel = values[1] as DatabindModel;
                                if (dataBindModel != null)
                                {
                                    StringBuilder sBuilder = new StringBuilder();
                                    sBuilder.Append("Source activity: ");
                                    sBuilder.Append(dataBindModel.SourceActivityName);
                                    sBuilder.Append(Environment.NewLine);
                                    sBuilder.Append("Source property: ");
                                    sBuilder.Append(dataBindModel.SourcePropertyName);
                                    retVal = sBuilder.ToString();
                                }
                            }

                            break;

                        case "comboBoxCtrl":
                            List<TridentPropertyBindingObject> valuesList = new List<TridentPropertyBindingObject>();
                            if (paramModel.DataType.Equals("System.Boolean"))
                            {
                                TridentPropertyBindingObject trueObj = new TridentPropertyBindingObject(true, "True", paramModel);
                                TridentPropertyBindingObject falseObj = new TridentPropertyBindingObject(false, "False", paramModel);
                                valuesList.Add(trueObj);
                                valuesList.Add(falseObj);
                            }

                            retVal = valuesList;
                            break;
                    }
                }
            }

            return retVal;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetTypes">
        /// The expected types.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old values.
        /// </returns>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }

        #endregion
    }

    /// <summary>
    /// Generates the unique id based on the parameter.
    /// </summary>
    public class UniqueIdGenerator : IValueConverter
    {
        #region IValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null)
            {
                StringBuilder uniqueName = new StringBuilder();
                uniqueName.Append("uniqueName_");
                uniqueName.Append(Math.Abs(value.GetHashCode()).ToString(CultureInfo.InvariantCulture));
                return uniqueName.ToString();
            }
            else
            {
                return "uniqueId";
            }
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }

        #endregion
    }

    /// <summary>
    /// Generates the unique id based on the parameter.
    /// </summary>
    public class MandatoryTextGenerator : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="values">
        /// The current values.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            ParameterDescriptionModel paramModel = parameter as ParameterDescriptionModel;
            return (paramModel.IsMandatory && paramModel.IsNotMandatoryForWorkflow) ? TridentResourceManager.GetString("PropertyPaneMandatoryYes") : TridentResourceManager.GetString("PropertyPaneMandatoryNo");
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }

        #endregion
    }

    /// <summary>
    /// Converts the width to specify the new width.
    /// </summary>
    public class WidthConverter : IValueConverter
    {
        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            double currWidth = -1;
            double delta = 10;

            if (parameter != null)
            {
                if (parameter.ToString().Equals("fromTextBox", StringComparison.OrdinalIgnoreCase))
                {
                    delta = -110;
                }
            }

            if (double.TryParse(value.ToString(), out currWidth))
            {
                return currWidth + delta;
            }

            return value;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
    }

    /// <summary>
    /// Specifies visibility specific to the values passed.
    /// </summary>
    public class VisibilityConverter : IValueConverter
    {
        #region IValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (parameter.ToString().Equals("ParamVisibility", StringComparison.OrdinalIgnoreCase)
                || parameter.ToString().Equals("ActivityVisibility", StringComparison.OrdinalIgnoreCase))
            {
                BaseModel model = value as BaseModel;
                if (model != null)
                {
                    return VisibilityConverter.FetchParamActivityVisibility(model, parameter.ToString());
                }
            }

            return Visibility.Visible;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old value.
        /// </returns>
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return value;
        }
        
        private static Visibility FetchParamActivityVisibility(BaseModel model, string flag)
        {
            Visibility vis = Visibility.Visible;
            if (flag.Equals("ParamVisibility", StringComparison.OrdinalIgnoreCase))
            {
                vis = model.VisibleOnUI.ActivityParametersVisibility;
            }
            else if (flag.Equals("ActivityVisibility", StringComparison.OrdinalIgnoreCase))
            {
                vis = model.VisibleOnUI.ActivityVisibility;
            }

            return vis;
        }

        #endregion 
    }

    /// <summary>
    /// Enables visibility calculation bsaed on multiple parameters.
    /// </summary>
    public class VisibilityMultiConverter : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="values">
        /// The current values.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            object retVal = null;
            if (parameter != null && values != null && values.Length > 1)
            {
                ParameterDescriptionModel paramModel = values[0] as ParameterDescriptionModel;
                if (paramModel != null)
                {
                    switch (parameter.ToString())
                    {
                        case "CustomType":
                        case "Editable":
                            retVal = VisibilityMultiConverter.FetchVisibility(paramModel, parameter.ToString());
                            break;

                        case "NonEditable":
                            retVal = paramModel.IsDatabound ? Visibility.Visible : Visibility.Collapsed;
                            break;
                    }
                }
            }

            return retVal;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetTypes">
        /// The expected types.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old values.
        /// </returns>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }

        private static object FetchVisibility(ParameterDescriptionModel paramModel, string parameter)
        {
            object retVal = null;
            if (paramModel.IsDatabound)
            {
                retVal = Visibility.Collapsed;
            }
            else
            {
                bool isCustomType = false;
                try
                {
                    TypeInitializer typeInit = TypeProviderHelper.FetchTypeInitializer(paramModel.DataType, Runtime.WPF);
                    isCustomType = typeInit != null;
                }
                catch (Exception)
                {
                }

                switch (parameter)
                {
                    case "CustomType":
                        retVal = isCustomType ? Visibility.Visible : Visibility.Collapsed;
                        break;
                    case "Editable":
                        retVal = isCustomType ? Visibility.Collapsed : Visibility.Visible;
                        break;
                }
            }

            return retVal;
        }

        #endregion
    }

    /// <summary>
    /// Enable the visibility of the asterix near the name of the property.
    /// </summary>
    public class AsterixVisibilityConverter : IMultiValueConverter
    {
        #region IMultiValueConverter Members

        /// <summary>
        /// Convert based on value and parameter.
        /// </summary>
        /// <param name="values">
        /// The current values.
        /// </param>
        /// <param name="targetType">
        /// The expected type.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Modified value.
        /// </returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            Visibility result = Visibility.Collapsed;
            bool isActivityMandatory = (bool)values[0];
            bool isWorkflowMandatory = (bool)values[1];
            if (isActivityMandatory && !isWorkflowMandatory)
            {
                result = Visibility.Visible;
            }
            return result;
        }

        /// <summary>
        /// Convert back based on value and parameter.
        /// </summary>
        /// <param name="value">
        /// The current value.
        /// </param>
        /// <param name="targetTypes">
        /// The expected types.
        /// </param>
        /// <param name="parameter">
        /// Passed parameter.
        /// </param>
        /// <param name="culture">
        /// Culture.
        /// </param>
        /// <returns>
        /// Old values.
        /// </returns>
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            return new object[] { value };
        }

        #endregion
    }

    /// <summary>
    /// The binding object that represents the drop down values.
    /// </summary>
    public class TridentPropertyBindingObject : Object
    {
        /// <summary>
        /// Gets and sets the display data.
        /// </summary>
        public object Key
        {
            get;
            set;
        }

        /// <summary>
        /// Gets and sets the actual data.
        /// </summary>
        public object Value
        {
            get;
            set;
        }

        /// <summary>
        /// The parameterDescriptionModel.
        /// </summary>
        public ParameterDescriptionModel ParameterDescriptionModel
        {
            get;
            set;
        }

        /// <summary>
        /// The binding object.
        /// </summary>
        /// <param name="key">
        /// The display data.
        /// </param>
        /// <param name="value">
        /// The actual data.
        /// </param>
        /// <param name="paramDesc">
        /// ParameterDescriptionModel.
        /// </param>
        public TridentPropertyBindingObject(object key, object value, ParameterDescriptionModel parameterDescription)
        {
            this.Key = key;
            this.Value = value;
            this.ParameterDescriptionModel = parameterDescription;
        }
    }
}
